package common.utils;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Properties;

import org.apache.tools.bzip2.CBZip2InputStream;
import org.apache.tools.tar.TarEntry;
import org.apache.tools.tar.TarInputStream;

// Should NOT be singletoned, this is a Utils class
public class CommonUtils {

	/**
	 * @param value
	 * @return wraps the string with quoutes as the name implies or null if value is null
	 */
    public static String wrapStringWithQuotes(String value) {
        return value == null ? null : "\"" + value + "\"";
    }
	
    /**
     * Replaces each string in the array with its trimmed value
     * @param statements
     * @return
     */
    public static String[] trimStatements(String[] statements) {
        for (int i=0; i<statements.length; i++) {
            statements[i] = statements[i].trim();
        }
        return statements;
    }
    
	/**
	 * This method checks if a given string is valid ascii.
	 * 
	 * @param str
	 * @return True if the given string is valid ascii
	 */
	public static boolean isValidAscii(String str) {
		if (str == null) {
		    return true;
		}
		for (int i=0; i<str.length(); i++) {
		    char c = str.charAt(i);
		    if (c >= ' ' && c <= '!') continue;
		    if (c >= ',' && c <= '.') continue;
		    if (c >= '0' && c <= ':') continue;
		    if (c == '?') continue;
		    if (c >= 'A' && c <= 'Z') continue;
		    if (c >= 'a' && c <= 'z') continue;
		    return false;
		}
		return true;
	}
	
	/**
	 * This method insert's the params into the template.</br>For example: 
	 * Template = "how <0> you <1>?" , Params = {"are","today"} , will return "how are you today?"
	 * 
	 * @param template - The template as described above.
	 * @param params - The parameters.
	 * @return Formated string.
	 */
	public static String formatString(String template, Object... params) {
		String result = template;
		for (int i = 0; i < params.length; i++) {
			result = result.replaceFirst("<" + i + ">", params[i].toString());
		}	
		return result;
	}
	
	/**
	 * This method appends a file separator to the path, if it doesn't already end with one.
	 */
	public static String appendSeparator(String path) {
		if (path.charAt(path.length() - 1) != File.separatorChar) {
			path += File.separatorChar;
		}
		return path;
	}
	
	/**
	 * This method downloads and extract the latest datadump of fictional universe from freebase.
	 * 
	 * @param outputPath - The output path.
	 * @return the root of the extracter archive
	 * @throws IOException
	 */
	public static File downloadAndExtractLatestDatadump(String outputPath) throws IOException {
		File compressedBZ2File = downloadLatestDatadump(outputPath);
		return extractDataDump(outputPath, compressedBZ2File, true);
	}
	
	   /**
     * This method extract the given datadump of fictional universe.
     * 
     * @param outputPath - The output path.
     * @return the root of the extracter archive
     * @throws IOException
     */
	public static File extractDataDump(String outputPath, File compressedBZ2File, boolean deleteBZ2) throws IOException {
	    File compressedTarFile = new File(appendSeparator(outputPath) + "fictional_universe.tar");
        extractBZ2(compressedBZ2File, compressedTarFile);   
        extractTar(compressedTarFile, outputPath);
        if (deleteBZ2) {
            compressedBZ2File.delete();
        }
        compressedTarFile.delete(); 
        return new File(appendSeparator(outputPath) + "fictional_universe");
	}
	
	/**
	 * This method download the latest datadump of fictional universe from freebase.
	 * 
	 * @param outputPath - The output path for the downloaded file.
	 * @return The downloaded file.
	 * @throws IOException
	 */
	public static File downloadLatestDatadump(String outputPath) throws IOException {
		File pathDir = new File(outputPath);
		if (!pathDir.exists()) {
			pathDir.mkdirs();
		}
		
		URL url = new URL("http://download.freebase.com/datadumps/latest/browse/fictional_universe.tar.bz2");
		BufferedInputStream in = new BufferedInputStream(url.openStream());
		File outputFile = new File(pathDir.getAbsolutePath() + File.separatorChar + "fictional_universe.tar.bz2");
		FileOutputStream fos = new FileOutputStream(outputFile);
		BufferedOutputStream bout = new BufferedOutputStream(fos, 1024);
		
		byte[] data = new byte[1024];
		int size = 0;
		while ((size = in.read(data, 0, 1024)) >= 0) {
			bout.write(data, 0, size);
		}
		
		bout.close();
		in.close();	
		return outputFile;
	}

	/**
	 * This method extract a BZ2 compressed file.
	 * 
	 * @param compressedBZ2 - The compressed file.
	 * @param outputFile - The output file.
	 * @throws IOException
	 */
	public static void extractBZ2(File compressedBZ2, File outputFile) throws IOException {	
		FileInputStream fin = new FileInputStream(compressedBZ2);
		fin.skip(2);
		CBZip2InputStream bzIn = new CBZip2InputStream(fin);
		FileOutputStream outputStream = new FileOutputStream(outputFile);
		
		byte[] data = new byte[1024];
		int size = 0;
		while ((size = bzIn.read(data, 0, 1024)) >= 0) {
			outputStream.write(data, 0, size);
		}
		
		outputStream.close();
		bzIn.close();
		fin.close();
	}

	/**
	 * This method extract a Tar compressed file.
	 * 
	 * @param CompressedTar - The compressed file.
	 * @param outputPath - The output path.
	 * @throws IOException
	 */
	public static void extractTar(File CompressedTar, String outputPath) throws IOException {
		FileInputStream compressedTarIn = new FileInputStream(CompressedTar);
		TarInputStream tarIn = new TarInputStream(compressedTarIn);
		File extractDir = new File(outputPath);
		if (!extractDir.exists()) {
			extractDir.mkdirs();
		}
		
		TarEntry tarEntry;
		for (tarEntry = tarIn.getNextEntry(); tarEntry != null; tarEntry = tarIn.getNextEntry()) {
			File destPath = new File(extractDir.getAbsolutePath() + File.separatorChar + tarEntry.getName());
			if (!tarEntry.isDirectory()) {
				FileOutputStream fout = new FileOutputStream(destPath);
				tarIn.copyEntryContents(fout);
				fout.close();
			} else {
				destPath.mkdir();
			}
		}
		
		tarIn.close();
		compressedTarIn.close();
	}

	/**
	 * This method non-recursively deletes the given directory.
	 * 
	 * @param directory - The directory to delete.
	 */
	public static boolean deleteDirectory(File directory) {
	    if (!directory.isDirectory()) {
	        return false;
	    }
	    
	    for (File file : directory.listFiles()) {
	        if (file.isFile()) {
	            file.delete();
	        }
	    }
	    
	    directory.delete();
	    return true;
	}
	
    /**
     * @param str
     * @return True if str is null OR empty
     */
    public static boolean isEmpty(String str) {
    	return str==null || str.isEmpty();
    }
    
	/**
	 * This method reads properties files.
	 * 
	 * @param filePath - The file path holding the properties file.
	 * @return The properties 
	 */
	public static Properties readPropertiesFile(String filePath) throws FileNotFoundException, IOException {
		Properties properties = new Properties();
		FileInputStream inStream = new FileInputStream(filePath);
		properties.load(inStream);
		inStream.close();
		return properties;
	}
	
	/**
	 * This method writes properties to a file.
	 * 
	 * @param properties - The properties to save.
	 * @param filePath - The location you want to save.
	 * @param comments - Comments about the propery file.
	 */
	public static void writePropertiesFile(Properties properties, String filePath, String comments) throws FileNotFoundException, IOException {
		FileOutputStream outStream = new FileOutputStream(filePath);
		properties.store(outStream, comments);
		outStream.close();
	}
}
